// +build !linux

package command

/*

#include <conio.h>
enum {
	GO_KEY_TAB = 9,//tag按键
	GO_KEY_CTRL_C = 3,//ctrl+c退出
	GO_KEY_ENTER = 13,//回车
	GO_KEY_DIR = 224,//方向键第一个
	GO_KEY_DIR2 = 91,//方向键第二个
	GO_KEY_UP = 72,//上
	GO_KEY_DOWN = 80,//下
	GO_KEY_LEFT = 75,//左
	GO_KEY_RIGHT = 77,//右
	GO_KEY_BREAK = 8,//break
	GO_KEY_DELETE = 83,//del
	GO_KEY_HOME = 71,//home
	GO_KEY_END = 79,//end
};


*/
import "C"

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"src/utils"
	"strings"
)

type Callback func(v ...interface{}) bool

type Element struct {
	//元素名
	name string
	//回调,如果为空就是个配置节点,否则就是命令
	callback Callback
	//描述
	desc string
	//配置
	conf interface{}
	//指定配置文件
	specific_conf_file string
	//该元素下的子命令
	sub_element map[string]*Element
}

//****************************全局变量************************************************************
var (
	g_top_element *Element                        //顶层命令
	g_stack       *utils.Stack = utils.NewStack() //节点栈
)

//const
const (
	const_path = "/conf"
	const_file = "/mysql_conf.txt"
)

//*****************************命令相关***********************************************************
func go_show(v ...interface{}) bool {
	t := g_stack.Peak()
	if t != nil {
		go_element_show(t.(*Element))
	}

	return true
}

func go_exit(v ...interface{}) bool {
	if g_stack.Len() > 1 {
		//一定要留一下
		g_stack.Pop()
	}

	return true
}

func go_quit(v ...interface{}) bool {
	fmt.Println("bye bye!!!")
	os.Exit(0)

	return true
}
func RegisterCommCmd(element *Element) {
	element.RegisterCmd("show", go_show, "显示当前节点下的子命令")
	element.RegisterCmd("exit", go_exit, "退回上一节点")
	element.RegisterCmd("quit", go_quit, "退出程序")
}

func go_element_show(element *Element) {
	//先打印节点名
	tmp := []string{}
	for _, ele := range element.sub_element {
		if ele.callback == nil {
			str := fmt.Sprintf("%-20s\t%s", ele.name, ele.desc)
			tmp = append(tmp, str)
		}
	}
	if len(tmp) > 0 {
		sort.Strings(tmp)
		fmt.Println("\r当前配置节点:\n")
		for _, name := range tmp {
			fmt.Printf("\r\t%s\n", name)
		}
	}
	tmp = []string{}
	//再打印命令名
	for _, ele := range element.sub_element {
		if ele.callback != nil {
			str := fmt.Sprintf("%-20s\t%s", ele.name, ele.desc)
			tmp = append(tmp, str)
		}
	}
	if len(tmp) > 0 {
		sort.Strings(tmp)
		fmt.Println("\r当前节点子命令:\n")
		for _, name := range tmp {
			fmt.Printf("\r\t%s\n", name)
		}
	}
	fmt.Println("\r")
}

func getOneElement() *Element {
	return &Element{"", nil, "", nil, "", make(map[string]*Element)}
}

func topRegisterElement() {
	if g_top_element == nil {
		g_top_element = getOneElement()
		g_top_element.name = "config"
		g_top_element.desc = "顶层命令"
		RegisterCommCmd(g_top_element)
	}
}

func RegisterElement(name string, desc string) *Element {
	topRegisterElement()
	return g_top_element.RegisterElement(name, desc)
}

func (ele *Element) RegisterElement(name string, desc string) *Element {
	dst := ele.RegisterCmd(name, nil, desc)
	RegisterCommCmd(dst)

	return dst
}

func (ele *Element) RegisterCmd(name string, callback Callback, desc string) *Element {
	element, ok := ele.sub_element[name]
	if ok {
		fmt.Println("\rname=", name, "is already register")
		return element
	}

	ele.sub_element[name] = getOneElement()
	ele.sub_element[name].name = name
	ele.sub_element[name].callback = callback
	ele.sub_element[name].desc = desc

	return ele.sub_element[name]
}

func register_config_show(v ...interface{}) bool {
	ele := v[0].(*Element)
	g_conf := ele.conf
	if ele.specific_conf_file != "" {
		fmt.Println("\rspecific_conf_file=>", ele.specific_conf_file)
	}
	data, err := json.MarshalIndent(g_conf, "", " ")
	if err != nil {
		fmt.Println("\rMarshalIndent失败")
	}
	fmt.Println(string(data))

	return true
}

func register_config_init(v ...interface{}) bool {
	ele := v[0].(*Element)
	g_conf := ele.conf

	//判断下这里有没有指定配置文件,如果有就用指定的
	path := ""
	ele.specific_conf_file = ""
	path = utils.GetExePath() + const_path + const_file

	if len(v) > 2 {
		sa := v[2].([]string)
		if len(sa) > 0 {
			ele.specific_conf_file = "/" + sa[0]
			path = utils.GetExePath() + const_path + ele.specific_conf_file
		}
	}
	fileptr, err := os.Open(path)
	if err != nil {
		return false
	}
	defer fileptr.Close()

	decoder := json.NewDecoder(fileptr)
	err = decoder.Decode(g_conf)
	if len(v) > 2 {
		if err != nil {
			fmt.Println("\rconf init ", path, " failed err=", err)
		} else {
			fmt.Println("\rconf init ", path, "succesfull")
		}
	}

	return true
}

func register_config_save(v ...interface{}) bool {
	ele := v[0].(*Element)
	g_conf := ele.conf

	dstpath := utils.GetExePath() + const_path
	dstfile := dstpath + const_file
	if ele.specific_conf_file != "" {
		dstfile = dstpath + ele.specific_conf_file
	}
	os.MkdirAll(dstpath, 0777)
	fileptr, err := os.Create(dstfile)
	if err != nil {
		fmt.Println("\ropen path=", dstfile, "failed, err=", err)
		return false
	}
	defer fileptr.Close()
	data, err := json.MarshalIndent(g_conf, "", " ")
	if err != nil {
		fmt.Println("\rMarshalIndent失败")
	} else {
		fmt.Println("\r配置保存成功")
	}

	fileptr.Write(data)

	return true
}

func (ele *Element) RegisterConf(conf interface{}) {
	ele.conf = conf
	if conf != nil {
		ele.RegisterCmd("config_init", register_config_init, "配置初始化")
		ele.RegisterCmd("config_save", register_config_save, "配置保存")
		ele.RegisterCmd("config_show", register_config_show, "显示当前配置参数,如当前数据库名,服务器ip等等信息")
		//先初始化一遍配置
		register_config_init(ele)
	}
}

func goNeedShow(str string) bool {
	if str == "help" ||
		str == "?" ||
		str == "-h" ||
		str == "." ||
		str == "h" ||
		str == "ls" {
		return true
	}

	return false
}

func goCmdProc(sa []string) {
	curele := g_stack.Peak()
	if curele == nil {
		return
	}

	//先简单判断一下
	if goNeedShow(sa[0]) {
		go_show()
		return
	}

	ele := curele.(*Element)
	for _, sele := range ele.sub_element {
		if sele.name == sa[0] {
			if sele.callback != nil {
				sele.callback(ele, sele, sa[1:])
			} else {
				g_stack.Push(sele)
			}
			return
		}
	}

	fmt.Println("\r" + sa[0] + " is not support")
}

//初始化函数
func init() {
	topRegisterElement()
	g_stack.Push(g_top_element)
}

func getPrefix() string {
	str := "\rshell"
	top := g_stack.Peak()
	if top != nil {
		str += " " + top.(*Element).name
	}
	str += "->"

	return str
}

func getArrayMinLen(sa []string) int {
	ll := 0
	for i := 0; i < len(sa); i++ {
		if i == 0 {
			ll = len(sa[i])
			continue
		}
		if ll > len(sa[i]) {
			ll = len(sa[i])
		}
	}

	return ll
}

func getSamePrefix(sa []string) string {
	minlen := getArrayMinLen(sa)
	for i := 0; i < minlen; i++ {
		dst := sa[0][0 : i+1]
		for j := 1; j < len(sa); j++ {
			if dst != sa[j][0:i+1] {
				return sa[0][0:i]
			}
		}
	}

	return sa[0][0:minlen]
}

func go_cmd_tab(ba []byte) string {
	top := g_stack.Peak().(*Element)
	blen := len(ba)
	if top == nil {
		return ""
	}

	sa := []string{}
	dststr := string(ba)
	for _, it := range top.sub_element {
		if blen == 0 {
			//全加
			sa = append(sa, it.name)
			continue
		}
		if len(dststr) > len(it.name) {
			continue
		}
		if dststr == it.name[0:blen] {
			sa = append(sa, it.name)
		}
	}
	if len(sa) == 0 {
		return ""
	}

	if len(sa) == 1 {
		return sa[0]
	}

	sort.Strings(sa)
	str := "\n"
	for i := 0; i < len(sa); i++ {
		str += fmt.Sprintf("%-35s\t", sa[i])
		if (i+1)%3 == 0 {
			str += fmt.Sprintf("\n")
		}
	}
	fmt.Println(str)

	//这里返回组里面最少匹配的字符串
	return getSamePrefix(sa)
}

func isValid(bt byte) bool {
	switch {
	case bt == '$':
		return true
	case bt == '#':
		return true
	case bt == '@':
		return true
	case bt == '+':
		return true
	case bt == '=':
		return true
	case bt == '"':
		return true
	case bt == '\'':
		return true
	case bt == '(':
		return true
	case bt == ')':
		return true
	case bt == '%':
		return true
	case bt == '?':
		return true
	case bt == '-':
		return true
	case bt == '_':
		return true
	case bt == ' ':
		return true
	case bt == '.':
		return true
	case bt == '*':
		return true
	case bt >= '0' && bt <= '9':
		return true
	case bt >= 'a' && bt <= 'z':
		return true
	case bt >= 'A' && bt <= 'Z':
		return true
	}

	return false
}

const (
	MAX_LAST_CMD_NUM = 30
)

type LastCmd struct {
	ca [][]byte
	ci int
}

func (l *LastCmd) Len() int {
	return len(l.ca)
}

func (l *LastCmd) Add(c []byte) int {
	//先找找,有的话就不添加了,只是更新下位置
	for i := 0; i < l.Len(); i++ {
		if string(c) == string(l.ca[i]) {
			l.ci = (i + 1) % l.Len()
			return l.ci
		}
	}
	if l.Len() < MAX_LAST_CMD_NUM {
		l.ca = append(l.ca, c)
		l.ci = 0
		return l.ci
	}
	l.ca[l.ci] = c
	l.ci = (l.ci + 1) % MAX_LAST_CMD_NUM

	return l.ci
}

func (l *LastCmd) Show() {
	fmt.Println("\r")
	for i := 0; i < l.Len(); i++ {
		fmt.Println("\ri=", i, string(l.ca[i]))
	}
	fmt.Println("\r")
}

func (l *LastCmd) IndexInc() {
	if l.Len() > 0 {
		l.ci++
		if l.ci >= l.Len() {
			l.ci = 0
		}
	} else {
		l.ci = 0
	}
}

func (l *LastCmd) IndexDec() {
	if l.Len() > 0 {
		l.ci--
		if l.ci < 0 {
			l.ci = l.Len() - 1
		}
	} else {
		l.ci = 0
	}
}

func (l *LastCmd) getCurCmd() []byte {
	if l.Len() > 0 {
		return l.ca[l.ci]
	}

	return []byte{}
}

var last_cmd LastCmd

func Run() {
	fmt.Println("\rwelcome to consol shell")
	var str string = ""
	var err error = nil
	for {
		fmt.Printf(getPrefix())
		str, err = readString()
		if err != nil {
			fmt.Println("\rfailed to ReadString ", err)
			os.Exit(-1)
		}
		str = strings.Trim(str, "\r\n")
		str = strings.TrimSpace(str)
		if str == "" {
			continue
		}
		last_cmd.Add([]byte(str))
		//拆分命令行
		sa := strings.Split(str, " ")
		//清除下空格
		i := 0
		for i = 0; i < len(sa); i++ {
			sa[i] = strings.TrimSpace(sa[i])
		}
		goCmdProc(sa)
	}
}

/***********************************windows专用********************************************************************/
func readString() (string, error) {
	ba := []byte{}
	bae := []byte{}
	var bt byte = 0xff
	var lbt byte = 0xff
	//可能windows和linux处理不一样...
	for {
		bt = byte(C.getch())
		// fmt.Println(bt)
		//这里判断下方向按键
		if lbt == byte(C.GO_KEY_DIR) {
			switch bt {
			case byte(C.GO_KEY_UP):
				last_cmd.IndexDec()
				t := last_cmd.getCurCmd()
				ba = t
			case byte(C.GO_KEY_DOWN):
				last_cmd.IndexInc()
				t := last_cmd.getCurCmd()
				ba = t
			case byte(C.GO_KEY_LEFT):
				if len(ba) > 0 {
					bae = append(bae, ba[len(ba)-1])
					ba = ba[0:(len(ba) - 1)]
				}
			case byte(C.GO_KEY_RIGHT):
				if len(bae) > 0 {
					ba = append(ba, bae[len(bae)-1])
					bae = bae[0:(len(bae) - 1)]
				}
			case byte(C.GO_KEY_DELETE):
				if len(bae) > 0 {
					bae = bae[0:(len(bae) - 1)]
				}
			case byte(C.GO_KEY_HOME):
				//home键光标直接到最前面
				if len(ba) > 0 {
					ba = utils.ByteArrayReverse(ba)
					bae = append(bae, ba...)
					ba = ba[0:0]
				}
			case byte(C.GO_KEY_END):
				//end键光标直接到最后面
				if len(bae) > 0 {
					bae = utils.ByteArrayReverse(bae)
					ba = append(ba, bae...)
					bae = bae[0:0]
				}
			}
		} else {
			if isValid(bt) {
				ba = append(ba, bt)
			}
			switch bt {
			case byte(C.GO_KEY_CTRL_C):
				os.Exit(0)
			case byte(C.GO_KEY_ENTER):
				fmt.Println("")
				goto end
			case byte(C.GO_KEY_TAB):
				st := go_cmd_tab(ba)
				if st != "" {
					ba = []byte(st)
				}
			case byte(C.GO_KEY_BREAK):
				if len(ba) > 0 {
					ba = ba[0:(len(ba) - 1)]
				}
			}
		}
		lbt = bt
		//清空行
		sf := "\r                                                                                        \r"
		fmt.Print(sf)
		//先打印全的,再打印前头部分
		sf = getPrefix()
		if len(bae) > 0 {
			bae2 := utils.ByteArrayReverse(bae)
			fmt.Print(sf + string(ba) + string(bae2))
		}
		fmt.Print("\r" + sf + string(ba))
	}

end:
	if len(bae) > 0 {
		bae = utils.ByteArrayReverse(bae)
		ba = append(ba, bae...)
	}
	return string(ba), nil
}
